<?php
// }}}
// {{{ MobileHTMLConv
//
/**
 * Convert HTML to XHTML-MP or C-HTML via PEAR_XML_HTMLSax3.
 *
 * A HTML is changed to XHTML or C-HTML using PEAR_XML_HTMLSax3.
 * Unnecessary elements and attributes are eliminated to shave a file size in the case.
 * It's possible to establish the elements and attributes which aren't eliminated
 * from an option of a plugin.
 * <img>tag is changed to a link to picture and indicates substitution text.
 * All <script>tags are eliminated in spite of the contents.
 * When <a>tag is doing a nest, only the outside link becomes most effective.
 *
 * @category   Nucleus CMS plugins
 * @package    NP_Mobile2
 * @author     shizuki <shizuki@kinezumi.net>
 * @author     NKJG <http://niku.suku.name/>
 * @copyright  2008 shizuki
 * @license    http://nucleuscms.org/license.txt GNU General Public License
 * @version    $Id: htmlconv.php 57 2009-03-03 17:53:55Z shizuki17xx $
 * @link       http://japan.nucleuscms.org/wiki/plugins:mobile2
 * @since      Class available since 0.0.1
 */
class MobileHTMLConv
{

	// {{{ properties

	/**
	 * Elements which isn't eliminated
	 *
	 * @var array
	 */
	var $nonRemoveTags  = array();

	/**
	 * Attributes which isn't eliminated
	 *
	 * @var array
	 */
	var $nonRemoveAttrs = array();

	/**
	 * (X)HTML tag attribute's quote remove flag
	 *
	 * @var bool
	 */
	var $quoteMode      = false;

	/**
	 * Image tag convert type
	 *
	 * @var string
	 */
	var $imageMode      = 'link';

	/**
	 * Mobile career name
	 *
	 * @var string
	 */
	var $careerName     = 'other';

	/**
	 * MIME Type for mobile browser
	 *
	 * @var string
	 */
	var $mimeType       = 'application/xhtml+xml';

	/**
	 * Elements by which it's admitted that it's an empty
	 *
	 * @var array
	 */
	var $emptyElements  = array();

	/**
	 * Elements even the contents eliminated perfectly.
	 *
	 * @var array
	 */
	var $voidElements   = array();

	/**
	 * A just before element is start-tag.
	 *
	 * @var bool
	 */
	var $isEmpty        = false;

	/**
	 * Nest level of the <a> element
	 *
	 * @var int
	 */
	var $aNestLevel     = 0;

	/**
	 * Nest level of the ignore element
	 *
	 * @var int
	 */
	var $voidLevel     = 0;

	/**
	 * As a result of the processing.
	 *
	 * @var array
	 */
	var $result         = '';

	// }}}
	// {{{ MobileHTMLConv($params)

	/**
	 * Initialization of a class.
	 *
	 * @param array
	 */
	function MobileHTMLConv($params)
	{
		$this->result         = '';
		$this->nonRemoveTags  = $params['nonRemoveTags'];
		$this->nonRemoveAttrs = $params['nonRemoveAttrs'];
		$this->quoteMode      = $params['quoteMode'];
		$this->imageMode      = $params['imageMode'];
		$this->mimeType       = $params['mimeType'];
		$this->careerName     = $params['careerName'];

		$this->emptyElements = array(
			'area',
			'base',
			'basefont',
			'bgsound',
			'br',
			'embed',
			'hr',
			'img',
			'input',
			'link',
			'meta',
			'param',
			'col',
		);

		$this->voidElements = array(
			'script',
		);
		if ($this->mimeType == 'application/xhtml+xml') {
			$this->result = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n";
		}
	}

	// }}}
	// {{{ escapeHandler(&$parser, $data)

	/**
	 * Add HTML doctype.
	 *
	 * @param
	 *
	 * @param 
	 *
	 */
	function escapeHandler(&$parser, $data)
	{
		if (preg_match("/doctype/i", $data) && $this->mimeType == 'application/xhtml+xml') {
			$this->result .= '<!DOCTYPE html PUBLIC "-//';
			switch (strtolower($this->careerName)) {
				case 'docomo':
					$this->result .= 'i-mode group (ja)//DTD XHTML i-XHTML(Locale/Ver.=ja/2.0) 1.0//EN"'
								   . ' "i-xhtml_4ja_10';
					break;
				case 'ezweb':
					$this->result .= 'OPENWAVE//DTD XHTML Mobile 1.0//EN"'
								   . ' "http://www.openwave.com/DTD/xhtml-mobile10';
					break;
				case 'softbank':
					$this->result .= 'J-PHONE//DTD XHTML Basic 1.0 Plus//EN"'
								   . ' "xhtml-basic10-plus';
					break;
				default:
					$this->result .= 'WAPFORUM//DTD XHTML Mobile 1.0//EN"'
								   . ' "http://www.wapforum.org/DTD/xhtml-mobile10';
					break;
			}
			$this->result .= '.dtd">';
		}
	}

	// }}}
	// {{{ openHandler(&$parser, $element, $attr)

	/**
	 * Process open tag.
	 *
	 * @param
	 *
	 * @param 
	 *     HTML element
	 * @param 
	 *     Element's attribute(s)
	 */
	function openHandler(&$parser, $element, $attr)
	{
		$result  = '';
		$element = strtolower($element);
		$result .= $this->endOfEmpty();
		if (in_array($element, $this->voidElement)) {
			$this->voidLevel++;
		} elseif ($this->voidLevel == 0 && in_array($element, $this->nonRemoveTags)) {
			switch ($element) {
				case 'img' :
					$result .= $this->openHandler_img($attr);
					break;
				case 'a' :
					$result .= $this->openHandler_a($attr);
					break;
				default :
					$result .= $this->openHandler_default($element, $attr);
					break;
			}
		}
		$this->result .= $result;
	}

	// }}}
	// {{{ openHandler_img($attr)

	/**
	 * Process img tag.
	 *
	 * @param 
	 *     Image element's attribute(s)
	 *
	 * @return string
	 *     HTML TAG
	 */
	function openHandler_img($attr)
	{
		$result  = '';
		$altText = '';
		if (isset($attr['alt'])) {
			$altText = $attr['alt'];
		}
		switch ($this->imageMode) {
			case 'text':
				if ($altText !== '') {
					$result       .= $altText;
				}
				break;
			case 'link':
				if ($altText === '') {
					$altText = '[image]';
				}
				$newAttr         = $attr;
				$newAttr['href'] = $newAttr['src'];
				unset($newAttr['src']);
				unset($newAttr['alt']);
				$result .= $this->openHandler_a($newAttr);
				$result .= $altText;
				break;
			default:
				$result .= $this->openHandler_default('img', $attr);
		}
		return $result;
	}

	// }}}
	// {{{ openHandler_a($attr)

	/**
	 * Process img tag.
	 *
	 * @param 
	 *     A element's attribute(s)
	 *
	 * @return string
	 *     HTML TAG
	 */
	function openHandler_a($attr)
	{
		$result = '';
		if ($this->aNestLevel == 0) {
			$result .= $this->openHandler_default('a', $attr);
		}
		$this->aNestLevel++;
		return $result;
	}

	// }}}
	// {{{ openHandler_default($element, $attr)

	/**
	 * Process img tag.
	 *
	 * @param 
	 *     HTML element
	 * @param 
	 *     Element's attribute(s)
	 *
	 * @return string
	 *     HTML TAG
	 */
	function openHandler_default($element, $attr)
	{
		$emptyAttrs = array(
			'checked',
			'disabled',
			'readonry',
			'ismap',
			'multiple',
			'selected',
		);
		$result  = '';
		$tagName = strtolower($element);
		$close   = '';
		if (in_array($element, $this->emptyElement)) {
			$this->isEmpty = true;
		} else {
			$this->isEmpty = false;
		}
		$result .= '<' . $element;
		foreach ($attr as $name => $value) {
			$name = strtolower($name);
			if (in_array($name, $this->nonRemoveAttrs)) {
				switch ($this->mimeType) {
					case 'application/xhtml+xml':
						if (in_array($name, $emptyAttrs) && empty($value)) {
							$value = $name;
						}
						$result .= ' ' . $name . '="' . $value . '"';
						break;
					case 'text/html':
						if (in_array($name, $emptyAttrs)) {
							$result .= " $name";
						} elseif ($this->quoteMode
								&& !empty($value)
								&& strpos($value, ' ') === FALSE) {
							$result .= " $name=$value";
							if (!$this->isEmpty) {
								$close = ' ';
							}
						} else {
							$result .= ' ' . $name . '="' . $value . '"';
						}
						break;
				}
			}
		}
		if (!$this->isEmpty) {
			$close .= '>';
		}
		$result .= $close;
		return $result;
	}

	// }}}
	// {{{ closeHandler(&$parser, $element)

	/**
	 * Process close tag.
	 *
	 * @param
	 *
	 * @param 
	 *     HTML element
	 */
	function closeHandler(&$parser, $element)
	{
		$result  = '';
		$tagName = strtolower($element);
		if (in_array($tagName, $this->voidElement)) {
			$this->voidLevel--;
		} else if ($this->voidLevel == 0 && in_array($element, $this->nonRemoveTags)) {
			switch ($element) {
				case 'img' :
					$result .= $this->closeHandler_img();
					break;
				case 'a' :
					$result .= $this->closeHandler_a();
					break;
				default :
					$result .= $this->closeHandler_default($element);
					break;
			}
		}
		$this->result .= $result;
	}

	// }}}
	// {{{ closeHandler_img()

	/**
	 * Process img tag.
	 *
	 * @return string
	 *     HTML TAG
	 */
	function closeHandler_img()
	{
		$result = '';
		switch ($this->imageMode) {
			case 'text':
				break;
			case 'link':
				$result .= $this->closeHandler_a();
				break;
			default:
				$result .= $this->closeHandler_default('img');
		}
		return $result;
	}

	// }}}
	// {{{ closeHandler_a()

	/**
	 * Process a tag.
	 *
	 * @return string
	 *     HTML TAG
	 */
	function closeHandler_a()
	{
		$result = '';
		$this->aNestLevel--;
		if ($this->aNestLevel == 0) {
			$result .= $this->closeHandler_default('a');
		}
		return $result;
	}

	// }}}
	// {{{ closeHandler_default($element)

	/**
	 * Process close tag.
	 *
	 * @param 
	 *     HTML element
	 *
	 * @return string
	 *     HTML TAG
	 */
	function closeHandler_default($element)
	{
		$result  = '';
		$tagName = strtolower($element);
		if ($this->isEmpty) {
			switch ($this->mimeType) {
				case 'application/xhtml+xml':
					$result .= (' />');
					break;
				case 'text/html':
					$result .= ('>');
					break;
			}
		} else {
			$result .= ('</' . $element . '>');
		}
		$this->isEmpty = false;
		return $result;
	}

	// }}}
	// {{{ dataHandler(&$parser, $data)

	/**
	 * When it's in the script tag, everything is eliminated,
	 * and when it isn't so, it's returned just as it is.
	 *
	 * @param 
	 *
	 *
	 * @param string
	 *     HTML contents text
	 */
	function dataHandler(&$parser, $data)
	{
		$result  = '';
		$result .= $this->endOfEmpty();
		if ($this->voidLevel == 0) {
			$result .= $data;
		}
		$this->result .= $result;
	}

	// }}}
	// {{{ endOfEmpty()

	/**
	 * Empty tag is closed.
	 *
	 * @return string
	 *     TAG tail
	 */
	function endOfEmpty()
	{
		$result = '';
		if ($this->isEmpty) {
			switch ($this->mimeType) {
				case 'application/xhtml+xml':
					$result .= (' />');
					break;
				case 'text/html':
					$result .= ('>');
					break;
			}
		}
		$this->isEmpty = false;
		return $result;
	}

	// }}}
	// {{{ getResult()

	/**
	 * A result is faired, and it outputs.
	 *
	 * @return string
	 *     HTML contents
	 */
	function getResult()
	{
		$strHTML = $this->result;
		$strHTML = preg_replace("!(\r|\n|\n\r)!", "\n", $strHTML);
		$strHTML = preg_replace("!\n\s+!", "\n", $strHTML);
		$strHTML = preg_replace("!\s+\n!", "\n", $strHTML);
		$strHTML = preg_replace("!\n+!", "\n", $strHTML);
		$strHTML = preg_replace("!^\n!", "", $strHTML);
		return $strHTML;
	}




















}

